Skip to content

feat(cli): runs export + invoke --scaffold + envelope contract test vs cloud (#189)#192

Merged
kjgbot merged 5 commits into
mainfrom
feat/runs-export-scaffold
Jun 4, 2026
Merged

feat(cli): runs export + invoke --scaffold + envelope contract test vs cloud (#189)#192
kjgbot merged 5 commits into
mainfrom
feat/runs-export-scaffold

Conversation

@kjgbot

@kjgbot kjgbot commented Jun 4, 2026

Copy link
Copy Markdown
Contributor

User description

Summary

#189 P2, consuming cloud#1841 (merged): fixture discoverability for invoke.

  • agentworkforce runs export <runId> [--fixture event.json] [--agent <selector>] — pulls the byte-exact delivered envelope from cloud's GET /runs/:runId/envelope and writes the single-envelope fixture invoke consumes. The fixture IS cloud's normalized output, so replay can't drift. Agent-scoped endpoint → without --agent, every workspace agent is probed for the run id (bounded by agent count). Omitted/pre-capture runs get actionable errors pointing at --scaffold; a contract-violating captured:true + envelope:null response is refused, never fabricated (interpretEnvelopeResponse, exported + unit-tested). Auth/fetch reuses the deployments list|logs helpers (now exported from list-command.ts).
  • agentworkforce invoke --scaffold <type> — cold-start skeleton: frame filled (name/cron for cron.tick), resource an explicit TODO hole for provider events (per the issue: its shape is adapter-normalization + buildEnvelope territory and must not be hand-guessed). <type> validated against KNOWN_TRIGGER_CATALOG, warn-don't-block.
  • Envelope contract vs cloud: RawGatewayEnvelope gains the 5 missing cloud fields (provider, eventType, deliveryId, paths, resumeContext); RAW_GATEWAY_ENVELOPE_FIELDS is compile-checked against the type (satisfies keyof); envelope-fields.cloud.ts is the checked-in copy of cloud's merged ENVELOPE_FIELDS anchor (source + update procedure in the header); shim.contract.test.ts asserts every cloud field is documented AND that a full cloud-shaped envelope still shims/dispatches. Cloud's pin test (cloud#1851) names this copy in its failure message — drift fails CI on whichever side moves first.
  • README: fixture-provenance section (export-real-fire primary, scaffold cold-start, omit-never-truncate).

Tests

Runtime 52/52 (3 new contract tests) · CLI 240/240 (11 new: parse contracts, captured/omitted/pre-capture/violating-response interpretation, scaffold frame + TODO hole + warn-don't-block + e2e through runInvoke). Full pnpm run check green (fresh worktree, real pnpm install, workspace deps built — no hand-linking).

Issue mapping

  • runs export — real fire → replayable fixture (primary path)
  • --scaffold — cold-start skeleton with catalog validation
  • Cross-repo contract test pinning cloud's buildEnvelope fields vs RawGatewayEnvelope
  • Out of scope per issue: hand-maintained payload schemas / bundled samples (deferred to codegen)

Refs #189, AgentWorkforce/cloud#1841

🤖 Generated with Claude Code


CodeAnt-AI Description

Export real run envelopes and scaffold fixture files from the CLI

What Changed

  • Added runs export <runId> to download the exact envelope delivered to a run and save it as a replayable invoke fixture, or print it to stdout
  • Export now checks the run across workspace agents when no agent is specified, and shows clear errors when the run was never captured or the envelope was too large to store
  • Added invoke --scaffold <type> to generate a starter fixture without needing a persona or a real run, with a placeholder resource section for provider events
  • Updated the CLI help and README to point users to the new export-first fixture workflow

Impact

✅ Replay fixtures that match real runs
✅ Faster fixture creation before the first live event
✅ Clearer guidance for missing or oversized run envelopes

💡 Usage Guide

Checking Your Pull Request

Every time you make a pull request, our system automatically looks through it. We check for security issues, mistakes in how you're setting up your infrastructure, and common code problems. We do this to make sure your changes are solid and won't cause any trouble later.

Talking to CodeAnt AI

Got a question or need a hand with something in your pull request? You can easily get in touch with CodeAnt AI right here. Just type the following in a comment on your pull request, and replace "Your question here" with whatever you want to ask:

@codeant-ai ask: Your question here

This lets you have a chat with CodeAnt AI about your pull request, making it easier to understand and improve your code.

Example

@codeant-ai ask: Can you suggest a safer alternative to storing this secret?

Preserve Org Learnings with CodeAnt

You can record team preferences so CodeAnt AI applies them in future reviews. Reply directly to the specific CodeAnt AI suggestion (in the same thread) and replace "Your feedback here" with your input:

@codeant-ai: Your feedback here

This helps CodeAnt AI learn and adapt to your team's coding style and standards.

Example

@codeant-ai: Do not flag unused imports.

Retrigger review

Ask CodeAnt AI to review the PR again, by typing:

@codeant-ai: review

Check Your Repository Health

To analyze the health of your code repository, visit our dashboard at https://app.codeant.ai. This tool helps you identify potential issues and areas for improvement in your codebase, ensuring your repository maintains high standards of code health.

…s cloud (#189)

P2 of #189, consuming cloud#1841's envelope capture:

- `agentworkforce runs export <runId> [--fixture event.json] [--agent
  <selector>]` — pulls the byte-exact envelope cloud delivered to a run
  from GET /runs/:runId/envelope and writes it as the single-envelope
  fixture `invoke` consumes. The endpoint is agent-scoped, so without
  --agent every workspace agent is probed for the run id (bounded).
  Omitted (oversized) and pre-capture runs get user-actionable errors
  pointing at --scaffold; a contract-violating captured:true/null
  response is refused, never fabricated (interpretEnvelopeResponse,
  exported + unit-tested). Reuses the deployments list/logs auth + fetch
  helpers (now exported from list-command).

- `agentworkforce invoke --scaffold <type>` — cold-start fixture
  skeleton: frame filled (name/cron for cron.tick), `resource` left as
  an explicit TODO hole for provider events (its shape is decided by
  adapter normalization + cloud buildEnvelope and shouldn't be
  guessed). <type> validated against KNOWN_TRIGGER_CATALOG with the
  lintTriggers warn-don't-block stance.

- Envelope contract vs cloud: RawGatewayEnvelope gains the 5 fields
  cloud's buildEnvelope emits that the type lacked (provider,
  eventType, deliveryId, paths, resumeContext) + a
  RAW_GATEWAY_ENVELOPE_FIELDS const compile-checked against the type
  (`satisfies keyof`). envelope-fields.cloud.ts is the checked-in copy
  of cloud's ENVELOPE_FIELDS (source + update procedure in the header);
  shim.contract.test.ts asserts every cloud field is documented and
  that a full cloud-shaped envelope still shims/dispatches — drift now
  fails CI on whichever side moves first (cloud's pin test points
  here).

- README: fixture-provenance section (export-real-fire as the primary
  path, scaffold for cold start, omit-never-truncate noted).

Tests: runtime 52/52 (3 new contract), CLI 240/240 (11 new: parse,
captured/omitted/pre-capture/violating-response contracts, scaffold
frame/TODO-hole/warn-don't-block/e2e). Full `pnpm run check` green.

Refs #189, AgentWorkforce/cloud#1841

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@codeant-ai

codeant-ai Bot commented Jun 4, 2026

Copy link
Copy Markdown

CodeAnt AI is reviewing your PR.

@coderabbitai

coderabbitai Bot commented Jun 4, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@agent-relay-code[bot], we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 51 minutes and 31 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 75e690b9-234a-4c11-9d72-54fb17d39e09

📥 Commits

Reviewing files that changed from the base of the PR and between 95e5a5a and 221c594.

📒 Files selected for processing (2)
  • packages/cli/src/invoke-command.test.ts
  • packages/cli/src/invoke-command.ts
📝 Walkthrough

Walkthrough

Adds fixture sourcing and scaffold tooling: a new runs export command to capture real run envelopes, an invoke --scaffold generator to emit RawGatewayEnvelope skeletons, envelope contract anchors/tests, CLI wiring, and README documentation.

Changes

Fixture Export and Scaffold Workflow

Layer / File(s) Summary
Envelope Type Contracts and Cloud Integration
packages/runtime/src/shim.ts, packages/runtime/src/envelope-fields.cloud.ts, packages/runtime/src/shim.contract.test.ts
RawGatewayEnvelope gains optional metadata fields (provider, eventType, deliveryId, paths, resumeContext). RAW_GATEWAY_ENVELOPE_FIELDS list and CLOUD_ENVELOPE_FIELDS anchor enforce field coverage; contract tests verify coverage and shim behavior.
Scaffold Fixture Generator
packages/cli/src/invoke-command.ts, packages/cli/src/invoke-command.test.ts
invoke --scaffold <type> parses without persona/fixture, generates RawGatewayEnvelope skeletons via scaffoldFixture(), special-cases cron.tick, validates provider/event against KNOWN_TRIGGER_CATALOG (warn-only for unknown), and writes JSON skeletons with resource.TODO placeholders to file or stdout.
Runs Export Command Implementation
packages/cli/src/runs-command.ts, packages/cli/src/runs-command.test.ts
runs export <runId> parses args (--agent, --fixture, --workspace, --cloud-url, --no-prompt), resolves deployment context, fetches candidate agents, probes per-agent envelope endpoints (treating 404 as “not this agent”), interprets envelope responses into fixtures or user-facing errors, and emits fixture output and replay guidance.
CLI Wiring and Exports
packages/cli/src/list-command.ts, packages/cli/src/cli.ts, README.md
Export deployment/list helpers and types required by runs export, wire runs command group into CLI dispatch, and add README section describing fixture origins and --scaffold workflow.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related issues

Possibly related PRs

Poem

🐰 I found a run in clouds above,

I sketched a shell with careful love,
A TODO in the resource nest,
Exports and scaffolds now at rest,
Fixtures hum — your CLI's set to test.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 75.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the three main features added: runs export command, invoke --scaffold option, and envelope contract testing against cloud.
Description check ✅ Passed The description clearly explains the purpose, implementation details, and testing coverage of the three main features (runs export, invoke --scaffold, and envelope contract), with references to related issues and test results.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/runs-export-scaffold

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@codeant-ai codeant-ai Bot added the size:XL This PR changes 500-999 lines, ignoring generated files label Jun 4, 2026

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces the runs export command to retrieve gateway envelopes from cloud runs as replayable local fixtures, alongside a --scaffold option for generating skeleton fixtures. It also establishes a cross-repo contract test to prevent drift in envelope fields. Feedback on these changes suggests refining the CLI argument parser to support inline values for --workspace and --cloud-url, and modifying the agent probe's error handling to only ignore 404 errors rather than silently swallowing all exceptions.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread packages/cli/src/runs-command.ts Outdated
Comment on lines +191 to +195
} catch {
// 404 = not this agent's run (or auth/transient on this probe);
// keep scanning the remaining candidates.
continue;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The catch block silently swallows all errors during the agent runs probe. While skipping 404 Not Found errors is expected when scanning multiple agents, swallowing other critical errors (such as 401 Unauthorized, 500 Internal Server Error, or network timeouts) will hide the root cause and mislead the user into thinking the run ID simply does not exist. Only 404 errors should be caught and ignored to continue the scan.

Suggested change
} catch {
// 404 = not this agent's run (or auth/transient on this probe);
// keep scanning the remaining candidates.
continue;
}
} catch (err) {
// Only swallow 404 errors to continue scanning other candidates.
// Propagate other critical errors (e.g., 401, 500, network issues) immediately.
if (err instanceof Error && err.message.includes('failed: 404')) {
continue;
}
throw err;
}

Comment on lines +71 to +74
} else if (a === '--workspace') {
workspace = expectValue('--workspace', rest[++i]);
} else if (a === '--cloud-url') {
cloudUrl = expectValue('--cloud-url', rest[++i]);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The parser for runs export does not support inline values for --workspace and --cloud-url (e.g., --workspace=my-workspace or --cloud-url=http://...), which is inconsistent with other CLI commands and will cause the CLI to throw an 'unknown flag' error. Adding support for inline values ensures consistency and prevents unexpected crashes.

Suggested change
} else if (a === '--workspace') {
workspace = expectValue('--workspace', rest[++i]);
} else if (a === '--cloud-url') {
cloudUrl = expectValue('--cloud-url', rest[++i]);
} else if (a === '--workspace') {
workspace = expectValue('--workspace', rest[++i]);
} else if (a.startsWith('--workspace=')) {
workspace = expectInline('--workspace', a.slice('--workspace='.length));
} else if (a === '--cloud-url') {
cloudUrl = expectValue('--cloud-url', rest[++i]);
} else if (a.startsWith('--cloud-url=')) {
cloudUrl = expectInline('--cloud-url', a.slice('--cloud-url='.length));

Comment thread packages/cli/src/runs-command.ts Outdated
Comment on lines +191 to +203
} catch {
// 404 = not this agent's run (or auth/transient on this probe);
// keep scanning the remaining candidates.
continue;
}
}

if (!payload || !matchedAgent) {
throw new Error(
`run ${opts.runId} not found in workspace ${ctx.workspace}` +
`${opts.agent ? ` for agent "${opts.agent}"` : ` across ${candidates.length} agent(s)`}. ` +
'Check the run id (dashboard run detail, or `agentworkforce deployments list`).'
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 Architect Review — HIGH

The agent-probing loop in runRunsExport swallows all request failures from the envelope endpoint (including auth and server errors) and reports them as "run not found", so genuine cloud/API issues are misrepresented as a missing run.

Suggestion: Only continue probing on explicit not-found responses for that agent (e.g., 404), and immediately surface unauthorized/server/network failures with their original context; keep the "not found across N agents" error exclusively for the true all-404 case.

Fix in Cursor | Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is an **Architect / Logical Review** comment left during a code review. These reviews are first-class, important findings — not optional suggestions. Do NOT dismiss this as a 'big architectural change' just because the title says architect review; most of these can be resolved with a small, localized fix once the intent is understood.

**Path:** packages/cli/src/runs-command.ts
**Line:** 191:203
**Comment:**
	*HIGH: The agent-probing loop in `runRunsExport` swallows all request failures from the envelope endpoint (including auth and server errors) and reports them as "run not found", so genuine cloud/API issues are misrepresented as a missing run.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
If a suggested approach is provided above, use it as the authoritative instruction. If no explicit code suggestion is given, you MUST still draft and apply your own minimal, localized fix — do not punt back with 'no suggestion provided, review manually'. Keep the change as small as possible: add a guard clause, gate on a loading state, reorder an await, wrap in a conditional, etc. Do not refactor surrounding code or expand scope beyond the finding.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix

Comment on lines +419 to +431
id: 'evt_local_1',
workspace: 'ws-local',
type: 'cron.tick',
occurredAt,
name: 'TODO: your schedule name (persona schedules[].name)',
cron: '0 9 * * 1'
},
warnings
};
}

const firstDot = type.indexOf('.');
const provider = firstDot > 0 ? type.slice(0, firstDot) : '';

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: Any cron.* scaffold request is silently rewritten to cron.tick, so the generated fixture can represent a different event type than the user asked for. Preserve the requested type (or emit a warning/error) instead of coercing it. [incorrect variable usage]

Severity Level: Major ⚠️
- ⚠️ `invoke --scaffold` cron fixtures use incorrect type value.
- ⚠️ Mismatch between requested type and written fixture JSON.
Steps of Reproduction ✅
1. Invoke the CLI in scaffold mode, e.g. run `agentworkforce invoke --scaffold
cron.tick:my-schedule` so that `runInvoke()` in
`packages/cli/src/invoke-command.ts:261-289` is called with `args = ['--scaffold',
'cron.tick:my-schedule']`.

2. Inside `parseInvokeArgs()` at `packages/cli/src/invoke-command.ts:61-125`, the loop
sets `scaffoldType = 'cron.tick:my-schedule'` and, because `scaffoldType` is truthy,
returns `{ scaffold: scaffoldType, outputPath?: ... }` at lines 107-110 without requiring
a persona or fixture.

3. Back in `runInvoke()` at `packages/cli/src/invoke-command.ts:273-288`, the `'scaffold'
in opts` branch calls `scaffoldFixture(opts.scaffold)` with the original type string
`'cron.tick:my-schedule'` and then writes the returned `fixture` JSON to stdout or
`--output`.

4. In `scaffoldFixture()` at `packages/cli/src/invoke-command.ts:12-31` (diff lines
419-431), the condition `if (type === 'cron.tick' || type.startsWith('cron.'))` matches
`'cron.tick:my-schedule'`, but the constructed fixture hardcodes `"type": "cron.tick"`
instead of the requested `type` value, so the generated envelope's `type` field no longer
matches the string the user passed on the command line.

Fix in Cursor | Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** packages/cli/src/invoke-command.ts
**Line:** 419:431
**Comment:**
	*Incorrect Variable Usage: Any `cron.*` scaffold request is silently rewritten to `cron.tick`, so the generated fixture can represent a different event type than the user asked for. Preserve the requested type (or emit a warning/error) instead of coercing it.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

Comment on lines +73 to +78
} else if (a === '--cloud-url') {
cloudUrl = expectValue('--cloud-url', rest[++i]);
} else if (a === '--no-prompt') {
noPrompt = true;
} else if (a.startsWith('--')) {
throw new Error(`runs export: unknown flag "${a}"`);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: The parser accepts --cloud-url <value> but not --cloud-url=<value>, so users using the common inline form will get a hard failure. Add the inline branch to keep behavior consistent with other commands and avoid avoidable CLI failures. [api mismatch]

Severity Level: Major ⚠️
-`runs export` rejects inline `--cloud-url=` usage.
- ⚠️ Diverges from other CLI commands' flag handling.
Steps of Reproduction ✅
1. Invoke `agentworkforce runs export <runId> --cloud-url=https://cloud.example` so
`runRuns()` in `packages/cli/src/runs-command.ts:137-157` receives `args = ['export',
'<runId>', '--cloud-url=https://cloud.example']`.

2. `runRuns()` calls `parseRunsArgs()` at `packages/cli/src/runs-command.ts:43-101`; the
loop at lines 59-84 iterates over `'--cloud-url=https://cloud.example'`.

3. The parser only accepts the spaced form `a === '--cloud-url'` at line 73 and does not
have a `a.startsWith('--cloud-url=')` branch, unlike parsers in
`packages/cli/src/list-command.ts` which handle both `--cloud-url` and `--cloud-url=` for
deployment list and logs (see lines 205-208 and 251-254).

4. Because `'--cloud-url=https://cloud.example'` doesn't match the exact `--cloud-url`
form, it hits `else if (a.startsWith('--'))` at lines 77-78, throwing `runs export:
unknown flag "--cloud-url=https://cloud.example"`, which `runRuns()` surfaces as a usage
error instead of honoring the inline `--cloud-url=` format.

Fix in Cursor | Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** packages/cli/src/runs-command.ts
**Line:** 73:78
**Comment:**
	*Api Mismatch: The parser accepts `--cloud-url <value>` but not `--cloud-url=<value>`, so users using the common inline form will get a hard failure. Add the inline branch to keep behavior consistent with other commands and avoid avoidable CLI failures.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

Comment on lines +187 to +195
try {
payload = await requestJson<EnvelopeResponse>(url, ctx.token, 'runs export');
matchedAgent = agent;
break;
} catch {
// 404 = not this agent's run (or auth/transient on this probe);
// keep scanning the remaining candidates.
continue;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: The probe loop swallows every request failure and keeps scanning, so auth/network/server errors are misreported as "run not found". Only 404 should be treated as "not this agent"; for other statuses, propagate the error immediately so users get the real failure cause. [logic error]

Severity Level: Critical 🚨
-`runs export` hides backend or auth failures as not-found.
- ⚠️ Users misdiagnose expired tokens or server outages.
Steps of Reproduction ✅
1. Run `agentworkforce runs export <runId>` so the top-level CLI in
`packages/cli/src/cli.ts:73-77` dispatches to `runRuns()` in
`packages/cli/src/runs-command.ts:137-157` with arguments like `['export', '<runId>']`.

2. `runRuns()` calls `parseRunsArgs()` at `packages/cli/src/runs-command.ts:43-101` to
build `{ action: 'export', options }`, then calls `runRunsExport(parsed.options, io)` at
lines 151-153.

3. In `runRunsExport()` at `packages/cli/src/runs-command.ts:159-221`, the code builds a
list of candidate agents and enters the `for (const agent of candidates)` loop at lines
180-185, constructing an envelope URL per agent.

4. For each agent, it calls `requestJson<EnvelopeResponse>(url, ctx.token, 'runs export')`
inside the `try` block at lines 187-189; `requestJson()` (implemented in
`packages/cli/src/list-command.ts:409-418`) throws for any non-2xx response, including
401, 403, 500, and 404. The bare `catch { ... }` at
`packages/cli/src/runs-command.ts:191-195` swallows every such error and continues to the
next agent. If all probes fail (e.g. due to expired credentials causing 401 from every
agent), `payload` and `matchedAgent` remain null and the code throws a generic "run <id>
not found in workspace …" error at lines 198-203, misreporting authorization or server
failures as a missing run instead of surfacing the real backend error.

Fix in Cursor | Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** packages/cli/src/runs-command.ts
**Line:** 187:195
**Comment:**
	*Logic Error: The probe loop swallows every request failure and keeps scanning, so auth/network/server errors are misreported as "run not found". Only 404 should be treated as "not this agent"; for other statuses, propagate the error immediately so users get the real failure cause.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

Comment on lines +232 to +233
if (payload.captured === true && payload.envelope !== null && payload.envelope !== undefined) {
return { ok: true, fixture: `${JSON.stringify(payload.envelope, null, 2)}\n` };

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: A captured envelope is accepted as long as it is non-null, but non-object payloads (e.g. string/number) will still be exported and later fail when passed to invoke --fixture. Validate that the envelope is an object before exporting to avoid generating unusable fixtures. [type error]

Severity Level: Major ⚠️
-`runs export` may emit non-object fixtures unusable by invoke.
- ⚠️ Breaks replay pipeline between cloud exports and simulations.
Steps of Reproduction ✅
1. Assume the cloud envelope endpoint responds for some run with JSON like `{ "captured":
true, "envelope": "ok" }` (non-object envelope). `runRunsExport()` in
`packages/cli/src/runs-command.ts:159-221` fetches this via
`requestJson<EnvelopeResponse>()` at lines 187-189 and stores it in `payload`.

2. After finding a matching agent, `runRunsExport()` calls
`interpretEnvelopeResponse(payload, opts.runId, matchedAgent.deployedName)` at lines
206-207, passing the unvalidated `EnvelopeResponse` into `interpretEnvelopeResponse()`
defined at lines 227-251.

3. In `interpretEnvelopeResponse()` at `packages/cli/src/runs-command.ts:231-234`, the
first branch only checks `payload.captured === true` and `payload.envelope !== null &&
payload.envelope !== undefined`, without verifying that `envelope` is an object. For the
`"ok"` example, this branch returns `{ ok: true, fixture: "\"ok\"\n" }`, and
`runRunsExport()` writes that as the fixture file or stdout at lines 211-220.

4. When a user later replays the run with `agentworkforce invoke <persona> --fixture
exported.json`, `runInvokeWithOptions()` in `packages/cli/src/invoke-command.ts:299-360`
reads the fixture and calls `parseFixtureEnvelopes()` at lines 154-188. Since the fixture
text starts with a quote and not `{` or `[`, `parseFixtureEnvelopes()` falls through to
the final error at lines 186-188, throwing `invoke: fixture exported.json must be a JSON
envelope object, a JSON array, or NDJSON lines`. This shows that `runs export` can emit
fixtures that `invoke` cannot parse because `interpretEnvelopeResponse()` does not enforce
that `payload.envelope` is a JSON object envelope.

Fix in Cursor | Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** packages/cli/src/runs-command.ts
**Line:** 232:233
**Comment:**
	*Type Error: A captured envelope is accepted as long as it is non-null, but non-object payloads (e.g. string/number) will still be exported and later fail when passed to `invoke --fixture`. Validate that the envelope is an object before exporting to avoid generating unusable fixtures.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

@codeant-ai

codeant-ai Bot commented Jun 4, 2026

Copy link
Copy Markdown

CodeAnt AI finished reviewing your PR.

…nsient errors

reviewer-1's binding finding (gemini high, traced): the bare
catch{continue} laundered requestJson's 401 — which already carries the
actionable 'unauthorized. Run `agentworkforce login`' hint — into a
misleading 'run not found... check the run id'. The probe now continues
ONLY on a 404 (isProbeNotFound, exported + unit-tested with negative
controls) and rethrows everything else, which also makes the single
--agent candidate fail loud unconditionally. Same push: =-inline forms
for --workspace/--cloud-url (reviewer nit).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@agent-relay-code

Copy link
Copy Markdown
Contributor

⚠️ pr-reviewer push failed (exit 1) — fixes were not applied to the PR. The notes below are advisory and were not pushed.

Fixed the PR issues I found.

Changed:

Local verification:

  • corepack pnpm --filter @agentworkforce/cli test
  • corepack pnpm --filter @agentworkforce/runtime test
  • corepack pnpm run check

All passed locally.

…object envelopes

codeant actionable pair (lead-triaged, reviewer-confirmed): (1)
--scaffold cron.daily silently rewrote the fixture to cron.tick — a
DIFFERENT event type than asked; the requested type is now preserved
with a warning (the gateway emits cron.tick; the shim dispatches any
cron.*), consistent with warn-don't-block. (2)
interpretEnvelopeResponse accepted any non-null envelope, so a
contract-violating string/number/array would export as a 'fixture' that
fails later at invoke — the accept path now requires a JSON object,
same never-fabricate stance, with string/number/array negative
controls.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/cli/src/invoke-command.ts`:
- Around line 277-288: The scaffold branch performs async I/O (scaffoldFixture,
writeFile, io.stdout/stderr) outside the guarded invoke error path, so failures
bypass the normalized invoke error handling; fix this by ensuring the scaffold
branch is executed inside the same try/catch used by runInvoke (or wrap the
scaffold I/O in a try/catch that rethrows the caught error) so that writeFile or
io.* rejections propagate to the existing invoke error handler and produce the
normalized "invoke: ..." error and exit code; locate the scaffold branch
referencing opts.scaffold, scaffoldFixture, writeFile, io.stdout/io.stderr and
either move it into the main invoke try block or add a small try { ... await
writeFile(...) / io.stdout(...) } catch (err) { throw err } to let the outer
handler process it.

In `@packages/cli/src/runs-command.ts`:
- Around line 191-194: The blanket catch in the candidate-probing loop swallows
all errors; change the catch in runs-command.ts so it inspects the thrown error
(e.g., err.status or err.response?.status) and only continues the loop when the
HTTP status is 404, otherwise rethrow the error; update the catch around the
probe/fetch call (the try that currently ends with "continue;") to differentiate
404 vs non-404 failures and rethrow non-404 exceptions so auth/network/server
errors are not mistaken for "run not found".
- Around line 71-74: The argument parser currently only handles split-value
flags for workspace and cloudUrl; update the branch that checks a ===
'--workspace' and a === '--cloud-url' to also accept inline forms by checking if
a startsWith('--workspace=') or startsWith('--cloud-url=') and extracting the
substring after '=' (fallback to calling expectValue('--workspace', rest[++i]) /
expectValue('--cloud-url', rest[++i]) when no '=' is present). Use the same
variables workspace and cloudUrl and reuse expectValue for the split form;
ensure you trim/validate the extracted value the same way as expectValue does so
both inline and split forms behave identically.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: c3c19110-ccbf-44bd-a461-029722c3f26f

📥 Commits

Reviewing files that changed from the base of the PR and between f57270e and 54c5340.

📒 Files selected for processing (10)
  • README.md
  • packages/cli/src/cli.ts
  • packages/cli/src/invoke-command.test.ts
  • packages/cli/src/invoke-command.ts
  • packages/cli/src/list-command.ts
  • packages/cli/src/runs-command.test.ts
  • packages/cli/src/runs-command.ts
  • packages/runtime/src/envelope-fields.cloud.ts
  • packages/runtime/src/shim.contract.test.ts
  • packages/runtime/src/shim.ts

Comment on lines +277 to +288
if ('scaffold' in opts) {
const { fixture, warnings } = scaffoldFixture(opts.scaffold);
for (const warning of warnings) io.stderr(`warn: ${warning}\n`);
const text = `${JSON.stringify(fixture, null, 2)}\n`;
if (opts.outputPath) {
await writeFile(opts.outputPath, text, 'utf8');
io.stderr(`fixture skeleton written to ${opts.outputPath}\n`);
} else {
io.stdout(text);
}
return undefined;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Handle scaffold write failures through the same invoke error path.

--scaffold does async output I/O outside the guarded invoke try/catch, so output write failures can reject runInvoke directly instead of emitting a normalized invoke: ... error and setting exit code 1.

Suggested patch
-  if ('scaffold' in opts) {
-    const { fixture, warnings } = scaffoldFixture(opts.scaffold);
-    for (const warning of warnings) io.stderr(`warn: ${warning}\n`);
-    const text = `${JSON.stringify(fixture, null, 2)}\n`;
-    if (opts.outputPath) {
-      await writeFile(opts.outputPath, text, 'utf8');
-      io.stderr(`fixture skeleton written to ${opts.outputPath}\n`);
-    } else {
-      io.stdout(text);
-    }
-    return undefined;
-  }
+  if ('scaffold' in opts) {
+    try {
+      const { fixture, warnings } = scaffoldFixture(opts.scaffold);
+      for (const warning of warnings) io.stderr(`warn: ${warning}\n`);
+      const text = `${JSON.stringify(fixture, null, 2)}\n`;
+      if (opts.outputPath) {
+        await writeFile(opts.outputPath, text, 'utf8');
+        io.stderr(`fixture skeleton written to ${opts.outputPath}\n`);
+      } else {
+        io.stdout(text);
+      }
+      process.exitCode = 0;
+    } catch (err) {
+      io.stderr(`invoke: ${err instanceof Error ? err.message : String(err)}\n`);
+      process.exitCode = 1;
+    }
+    return undefined;
+  }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if ('scaffold' in opts) {
const { fixture, warnings } = scaffoldFixture(opts.scaffold);
for (const warning of warnings) io.stderr(`warn: ${warning}\n`);
const text = `${JSON.stringify(fixture, null, 2)}\n`;
if (opts.outputPath) {
await writeFile(opts.outputPath, text, 'utf8');
io.stderr(`fixture skeleton written to ${opts.outputPath}\n`);
} else {
io.stdout(text);
}
return undefined;
}
if ('scaffold' in opts) {
try {
const { fixture, warnings } = scaffoldFixture(opts.scaffold);
for (const warning of warnings) io.stderr(`warn: ${warning}\n`);
const text = `${JSON.stringify(fixture, null, 2)}\n`;
if (opts.outputPath) {
await writeFile(opts.outputPath, text, 'utf8');
io.stderr(`fixture skeleton written to ${opts.outputPath}\n`);
} else {
io.stdout(text);
}
process.exitCode = 0;
} catch (err) {
io.stderr(`invoke: ${err instanceof Error ? err.message : String(err)}\n`);
process.exitCode = 1;
}
return undefined;
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/cli/src/invoke-command.ts` around lines 277 - 288, The scaffold
branch performs async I/O (scaffoldFixture, writeFile, io.stdout/stderr) outside
the guarded invoke error path, so failures bypass the normalized invoke error
handling; fix this by ensuring the scaffold branch is executed inside the same
try/catch used by runInvoke (or wrap the scaffold I/O in a try/catch that
rethrows the caught error) so that writeFile or io.* rejections propagate to the
existing invoke error handler and produce the normalized "invoke: ..." error and
exit code; locate the scaffold branch referencing opts.scaffold,
scaffoldFixture, writeFile, io.stdout/io.stderr and either move it into the main
invoke try block or add a small try { ... await writeFile(...) / io.stdout(...)
} catch (err) { throw err } to let the outer handler process it.

Comment thread packages/cli/src/runs-command.ts
Comment thread packages/cli/src/runs-command.ts Outdated
@agent-relay-code

Copy link
Copy Markdown
Contributor

Fixed the PR issues I found:

Validation run locally:

  • corepack pnpm --filter @agentworkforce/cli test
  • corepack pnpm --filter @agentworkforce/runtime test
  • corepack pnpm run check

All completed successfully.

@agent-relay-code

Copy link
Copy Markdown
Contributor

pr-reviewer applied fixes — committed and pushed 95e5a5a to this PR. The notes below describe what changed.

Fixed the PR issues I found:

Validation run locally:

  • corepack pnpm --filter @agentworkforce/cli test
  • corepack pnpm --filter @agentworkforce/runtime test
  • corepack pnpm run check

All completed successfully.

@agent-relay-code

Copy link
Copy Markdown
Contributor

Reviewed PR #192 and made targeted fixes.

Fixed:

  • invoke --scaffold now rejects persona paths and run-only flags it would otherwise ignore: --fixture, --workspace, --input, --seed.
  • Scaffolded cron fixtures preserve the requested cron.* type instead of always emitting cron.tick.
  • Scaffold trigger validation now uses the same alias-aware known-trigger registry as lintTriggers, so aliases like google-mail work.
  • agentworkforce invoke --help now documents --scaffold.
  • Added regression tests for all of the above.

Validation run locally:

  • corepack pnpm --filter @agentworkforce/cli test
  • corepack pnpm --filter @agentworkforce/runtime test
  • corepack pnpm run check

All completed successfully.

@agent-relay-code

Copy link
Copy Markdown
Contributor

⚠️ pr-reviewer push failed (exit 1) — fixes were not applied to the PR. The notes below are advisory and were not pushed.

Reviewed PR #192 and made targeted fixes.

Fixed:

  • invoke --scaffold now rejects persona paths and run-only flags it would otherwise ignore: --fixture, --workspace, --input, --seed.
  • Scaffolded cron fixtures preserve the requested cron.* type instead of always emitting cron.tick.
  • Scaffold trigger validation now uses the same alias-aware known-trigger registry as lintTriggers, so aliases like google-mail work.
  • agentworkforce invoke --help now documents --scaffold.
  • Added regression tests for all of the above.

Validation run locally:

  • corepack pnpm --filter @agentworkforce/cli test
  • corepack pnpm --filter @agentworkforce/runtime test
  • corepack pnpm run check

All completed successfully.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/cli/src/invoke-command.ts (1)

282-293: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Reset process.exitCode on successful scaffold runs.

Line 282 returns from the scaffold path without setting a success code. If runInvoke is called multiple times in-process, a previous non-zero process.exitCode can leak into a successful --scaffold execution.

Suggested patch
   if ('scaffold' in opts) {
     const { fixture, warnings } = scaffoldFixture(opts.scaffold);
     for (const warning of warnings) io.stderr(`warn: ${warning}\n`);
     const text = `${JSON.stringify(fixture, null, 2)}\n`;
     if (opts.outputPath) {
       await writeFile(opts.outputPath, text, 'utf8');
       io.stderr(`fixture skeleton written to ${opts.outputPath}\n`);
     } else {
       io.stdout(text);
     }
+    process.exitCode = 0;
     return undefined;
   }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/cli/src/invoke-command.ts` around lines 282 - 293, The scaffold
branch returns early without clearing a prior non-zero exit status; inside the
if ('scaffold' in opts) block (around the scaffoldFixture/ writeFile/ io.stdout
logic), set process.exitCode = 0 just before the final return so a previous
failed run doesn't leak into subsequent in-process successful --scaffold runs;
update the code path that calls scaffoldFixture and returns undefined to reset
process.exitCode to 0 before returning.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@packages/cli/src/invoke-command.ts`:
- Around line 282-293: The scaffold branch returns early without clearing a
prior non-zero exit status; inside the if ('scaffold' in opts) block (around the
scaffoldFixture/ writeFile/ io.stdout logic), set process.exitCode = 0 just
before the final return so a previous failed run doesn't leak into subsequent
in-process successful --scaffold runs; update the code path that calls
scaffoldFixture and returns undefined to reset process.exitCode to 0 before
returning.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: d7056067-b65e-4bea-a77b-4bcf6374d862

📥 Commits

Reviewing files that changed from the base of the PR and between 54c5340 and 95e5a5a.

📒 Files selected for processing (4)
  • packages/cli/src/invoke-command.test.ts
  • packages/cli/src/invoke-command.ts
  • packages/cli/src/runs-command.test.ts
  • packages/cli/src/runs-command.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/cli/src/invoke-command.test.ts
  • packages/cli/src/runs-command.ts

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 4 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="packages/cli/src/runs-command.ts">

<violation number="1" location="packages/cli/src/runs-command.ts:282">
P2: `isProbeNotFound` uses regex matching on error message text instead of structured HTTP status, making probe retry/fail behavior brittle and potentially misclassifying non-404 errors.

(Based on your team's feedback about checking request errors by status instead of regex-parsing messages.) [FEEDBACK_USED].</violation>
</file>

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

* Exported for unit tests.
*/
export function isProbeNotFound(err: unknown): boolean {
return err instanceof Error && / failed: 404\b/.test(err.message);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: isProbeNotFound uses regex matching on error message text instead of structured HTTP status, making probe retry/fail behavior brittle and potentially misclassifying non-404 errors.

(Based on your team's feedback about checking request errors by status instead of regex-parsing messages.) .

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/cli/src/runs-command.ts, line 282:

<comment>`isProbeNotFound` uses regex matching on error message text instead of structured HTTP status, making probe retry/fail behavior brittle and potentially misclassifying non-404 errors.

(Based on your team's feedback about checking request errors by status instead of regex-parsing messages.) .</comment>

<file context>
@@ -249,3 +271,13 @@ export function interpretEnvelopeResponse(
+ * Exported for unit tests.
+ */
+export function isProbeNotFound(err: unknown): boolean {
+  return err instanceof Error && / failed: 404\b/.test(err.message);
+}
</file context>

@agent-relay-code

Copy link
Copy Markdown
Contributor

Reviewed PR #192 and made one fix: invoke --scaffold now rejects persona paths and invoke-only flags like --fixture, --input, --seed, and --workspace instead of silently ignoring them. Added regression coverage in packages/cli/src/invoke-command.test.ts.

Local validation passed:

  • corepack pnpm -r build
  • corepack pnpm --filter @agentworkforce/cli test
  • corepack pnpm --filter @agentworkforce/runtime test
  • corepack pnpm run check

@agent-relay-code

Copy link
Copy Markdown
Contributor

pr-reviewer applied fixes — committed and pushed 221c594 to this PR. The notes below describe what changed.

Reviewed PR #192 and made one fix: invoke --scaffold now rejects persona paths and invoke-only flags like --fixture, --input, --seed, and --workspace instead of silently ignoring them. Added regression coverage in packages/cli/src/invoke-command.test.ts.

Local validation passed:

  • corepack pnpm -r build
  • corepack pnpm --filter @agentworkforce/cli test
  • corepack pnpm --filter @agentworkforce/runtime test
  • corepack pnpm run check

@kjgbot kjgbot merged commit d4e0a8b into main Jun 4, 2026
3 checks passed
@kjgbot kjgbot deleted the feat/runs-export-scaffold branch June 4, 2026 09:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XL This PR changes 500-999 lines, ignoring generated files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant